#include "stdafx.h"

#include <fstream>

    
network::SBSession*			network::SBSession::m_singleton = NULL;
boost::asio::io_service*	network::SBSession::m_ioService = new boost::asio::io_service();

	namespace network
	{

		SBSession::SBSession(boost::asio::io_service& _ioService) :
			//boost::enable_shared_from_this<WeaselClientSession>(),
			Session(_ioService),
			m_resolver(_ioService),
			helper::MulticastReceiverHelper( boost::asio::ip::address::from_string("0.0.0.0"), boost::asio::ip::address::from_string("225.0.0.1") ),
			m_sessionState(_not_logged_),
			m_sessionId(0)
        {
			m_udpRequest = boost::thread(boost::bind( boost::mem_fn(&MulticastReceiverHelper::receive< SBSession >), this, &SBSession::handle_receive_from, this ));
			
			const char* buf[20] = { 0 };
			std::string address, port;
			std::ifstream connectionFile("network.cfg", std::ios::in );
			if( !connectionFile.is_open() )
			{
				std::cout<< "Error while opening file" << std::endl;
			}
			else
			{

				connectionFile >> address;
				//address.assign( buf );
				connectionFile >> port;
				//port.assign( buf );

				establishConnection( address, port );


				CONNECT( Event::EVENT_REQUEST_GENERIKS, &SBSession::sendPacketFromEvent );
				//CONNECT( Event::EVENT_NEED_GENERIK_REMOVE, &SBSession::eventToPacket );


				start();
				boost::shared_ptr< Event::EventRequestGeneriks > requestGeneriks = boost::make_shared< packet::RequestGeneriks >();
				POST_EVENT( requestGeneriks );

			}
			
        }

        SBSession::~SBSession()
        {
			m_udpRequest.join();
			m_networkListeningThread.join();

			//delete m_ioService;
        }

		bool SBSession::establishConnection( std::string _address, std::string _port)
		{
			boost::asio::ip::tcp::resolver::query	query(_address.c_str(), _port.c_str() );

			boost::asio::ip::tcp::resolver::iterator endpoint_iterator = m_resolver.resolve(query);
			boost::asio::ip::tcp::resolver::iterator end;

			boost::system::error_code error = boost::asio::error::host_not_found;
			while (error && endpoint_iterator != end)
			{
				network::Session::m_socket.close();
				network::Session::m_socket.connect(*endpoint_iterator++, error);
			}
			if (error)
			{
				throw boost::system::system_error(error);
				return false;
			}

#if defined( _DEBUG) && defined( _OUTPUT )
			std::cout << "Connected to " << _address << ":" << _port << std::endl;
#endif

			//return keyExchange();
			return true;
		}

		boost::uint16_t SBSession::getSessionId()
		{
			return m_sessionId;
		}

        void SBSession::start()
        {

            //std::cout << "New session" << std::endl;


            /*packet::WeaselKeyExchange shared_key(& (crypt.expendedKey()));
            first_packet_crypt().aes_crypt(shared_key);
            m_socket.async_write_some(shared_key,
                                      boost::bind(&SBSession::handle_write, shared_from_this(),
                                                  boost::asio::placeholders::error,
                                                  boost::asio::placeholders::bytes_transferred));

            first_packet_crypt().aes_decrypt(shared_key, 180);*/
            //buffer::hexDump(std::cout, *shared_key );

			//memset( &(*m_read_buffer), 0, 180 );
            // We listen for future reception
            /*boost::asio::async_read(	m_socket,boost::asio::buffer(*m_read_buffer), boost::asio::transfer_at_least(68),
                                                                    boost::bind(    &LoginSession::handle_read_first_packet,
                                                shared_from_this(),
                                                boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred));*/
			/*Session::m_socket.async_read_some(	boost::asio::buffer( *m_read_buffer ),
							boost::bind(&SBSession::handle_read,
                            shared_from_this(),
                            boost::asio::placeholders::error,
                            boost::asio::placeholders::bytes_transferred));*/

			m_networkListeningObject = SBSession::CallableNetworkListening<SBSession>( &SBSession::receivePacketToEvent, this );
			m_networkListeningThread = boost::thread(m_networkListeningObject);

        }

		void SBSession::sendPacketFromEvent( boost::shared_ptr< Event::EventBase> _evt )
		{

			boost::shared_ptr< buffer::BasicBuffer>  packet = boost::shared_dynamic_cast< buffer::BasicBuffer >( _evt );

			if( packet.get() && PacketManager::getSingleton().checkPacketId( packet.get() ) )
			{
					Session::m_socket.async_write_some(	*packet,											
					boost::bind(&SBSession::handle_write, this,		
														  boost::asio::placeholders::error,	
														   boost::asio::placeholders::bytes_transferred));	
				
			}
		}

		void SBSession::receivePacketToEvent( )
		{
			boost::system::error_code e;
			while( e != boost::asio::error::operation_aborted )
			{
				std::size_t bytes_transferred = Session::m_socket.read_some(	boost::asio::buffer( *m_read_buffer ), e );
				if( !e )
				{
					boost::uint16_t packetSize = 0;
					memcpy( &packetSize, &((*m_read_buffer)[0]), sizeof( boost::uint16_t ) );
					handle_read( e,	packetSize);
				}
#if defined(_DEBUG) && defined(_OUTPUT)
				else
					std::cout << "[ WeaselClientSession::receivePacketToEvent ] Error read some : " << e.message() << std::endl;
#endif
			}

#if defined(_DEBUG) && defined(_OUTPUT)
			std::cout << "Session stoppé : " << e.message() << std::endl;			
#endif

		}

		void SBSession::stop()
        {
#if defined(_DEBUG) && defined(_OUTPUT)
            std::cout << "Fin session" << std::endl;
#endif
			
			m_udpRequest.interrupt();
			Session::stop();
        }

		void SBSession::handle_receive_from(const boost::system::error_code& error, size_t bytes_recvd)
		{
			if (!error)
			{
				if( bytes_recvd > 0 )
				{
					PacketManager::getSingleton().resolve( shared_from_this(), m_data, bytes_recvd);
					
	#if defined(_DEBUG) && defined(_OUTPUT)
					std::cout << "Client " << m_sessionId << " receive from: " << m_senderEndpoint << std::endl;
	#endif
				}
			}
			else if (error == boost::asio::error::operation_aborted)
            {
				stop();
            }
	
			MulticastReceiverHelper::m_socket.async_receive_from(
					boost::asio::buffer(*m_data, SB_BUF_MAX), m_senderEndpoint,
					boost::bind( &SBSession::handle_receive_from, this,
					boost::asio::placeholders::error,
					boost::asio::placeholders::bytes_transferred)
					);

		}

        void SBSession::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
        {

            if (!e)
            {

                if ( bytes_transferred > 0 )
                {
                           

					// Call to the parser to compute input data, it will do a callback toward the good function on_packet
					// No crypto wioth char-server
					//if ( m_sessionState != _inter_server_logged_ )
						PacketManager::getSingleton().resolve(shared_from_this(), m_read_buffer, bytes_transferred); // Doit comporté une crypto
					//else
					//PacketManager::getSingleton().resolve(shared_from_this(), crypt.aes_decrypt(m_read_buffer, bytes_transferred), bytes_transferred);

					//m_read_buffer.reset();
					Session::m_socket.async_read_some(boost::asio::buffer(*m_read_buffer),
											 boost::bind(&SBSession::handle_read, shared_from_this(),
														 boost::asio::placeholders::error,
														 boost::asio::placeholders::bytes_transferred));
				}

            }
            else if (e == boost::asio::error::operation_aborted)
            {
#if defined(_DEBUG) && defined(_OUTPUT)
				std::cout << "Error while reading socket stream : " << e.message() << std::endl;
#endif
				stop();
            }



        }

        void SBSession::handle_read_first_packet(const boost::system::error_code& e, std::size_t bytes_transferred)
        {

            if (!e && !m_stopRequesting)
            {
                /* if ( bytes_transferred < MIN_LOGIN_SESSION_PACKETS_SIZE || bytes_transferred > MAX_LOGIN_SESSION_PACKETS_SIZE )
                 {
                 m_session_manager.stop(shared_from_this());
                        return;
                 }*/

                // Call to the parser to compute input data, it will do a callback toward the good function on_packet
                //PacketManager::getSingleton().resolve(shared_from_this(), first_packet_crypt().aes_decrypt(m_read_buffer, bytes_transferred), bytes_transferred); // Doit comporté une crypto
			
                //m_read_buffer.reset();

				Session::m_socket.async_read_some(boost::asio::buffer(*m_read_buffer),
                                         boost::bind(&SBSession::handle_read, shared_from_this(),
                                                     boost::asio::placeholders::error,
                                                     boost::asio::placeholders::bytes_transferred));
            }
            else if (e != boost::asio::error::operation_aborted)
            {
                stop();
            }
        }

        void SBSession::handle_write(const boost::system::error_code& e, std::size_t bytes_transferred)
        {

            if (!e)
            {
#if defined(_DEBUG) && defined(_OUTPUT)
                std::cout << "sb session writed ( size : " << bytes_transferred << " )" << std::endl;
#endif

				Session::m_socket.async_read_some(boost::asio::buffer(*m_read_buffer),
                                         boost::bind(&SBSession::handle_read, shared_from_this(),
                                                     boost::asio::placeholders::error,
                                                     boost::asio::placeholders::bytes_transferred));
		    }
            else if (e != boost::asio::error::operation_aborted)
            {
#if defined(_DEBUG) && defined(_OUTPUT)
                std::cout << e.message() << std::endl;
#endif
                stop();

            }

        }

		void SBSession::on_packet(buffer::BasicBuffer& _packet)
        {

			if( _packet.getId() == Event::EVENT_CHANGE_POSITION )
			{
				network::packet::RequestChangePosition& requestChangePosition = dynamic_cast< network::packet::RequestChangePosition& >( _packet );
				boost::shared_ptr< Event::EventChangePosition > eventChangePosition = boost::make_shared< Event::EventChangePosition >( requestChangePosition );
				POST_EVENT( eventChangePosition );
			}
			else if( _packet.getId() == Event::EVENT_GROW )
			{
				network::packet::RequestGrow& requestGrow = dynamic_cast< network::packet::RequestGrow& >( _packet );
				boost::shared_ptr< Event::EventGrow > eventGrow = boost::make_shared< Event::EventGrow >( requestGrow );
				POST_EVENT( eventGrow );
			}
			else if( _packet.getId() == Event::EVENT_NEED_GENERIK_ADD )
			{
				network::packet::RequestNeedGenerikAdd& requestNeedGenerikAdd = dynamic_cast< network::packet::RequestNeedGenerikAdd& >( _packet );
				boost::shared_ptr< Event::EventNeedGenerikAdd > eventNeedGenerikAdd = boost::make_shared< Event::EventNeedGenerikAdd >( requestNeedGenerikAdd );
				POST_EVENT( eventNeedGenerikAdd );
			}
			else if( _packet.getId() == Event::EVENT_NEED_GENERIK_REMOVE )
			{
				network::packet::RequestNeedGenerikRemove& requestNeedGenerikRemove = dynamic_cast< network::packet::RequestNeedGenerikRemove& >( _packet );
				boost::shared_ptr< Event::EventNeedGenerikRemove > eventNeedGenerikRemove = boost::make_shared< Event::EventNeedGenerikRemove >( requestNeedGenerikRemove );
				POST_EVENT( eventNeedGenerikRemove );
			}
			else if( _packet.getId() == Event::EVENT_NEED_GENERIK_SCENT_ADD )
			{
				network::packet::RequestNeedGenerikScentAdd& requestNeedGenerikScentAdd = dynamic_cast< network::packet::RequestNeedGenerikScentAdd& >( _packet );
				boost::shared_ptr< Event::EventNeedGenerikScentAdd > eventNeedGenerikScentAdd = boost::make_shared< Event::EventNeedGenerikScentAdd >( requestNeedGenerikScentAdd );
				POST_EVENT( eventNeedGenerikScentAdd );
			}





#if defined(_DEBUG) && defined(_OUTPUT)
            std::cout << " Raw data" << std::endl;
            std::cout << " Id = 0x0" << _packet.getId() << std::endl;
#endif
        }

        void SBSession::on_packet(packet::CancelOperation& _packet)
        {
#if defined(_DEBUG) && defined(_OUTPUT)
            std::cout << " handle reception packet Session: unspecialize packet!" << std::endl;
            std::cout << " Id = 0x0" << _packet.getId() << std::endl;
#endif
			m_stopRequesting = true;   
        }


    } // // namespace network
